/** @file bt_rx.c
  *
  * @brief This file contains the RX handling functions 
  * of Bluetooth driver.
  * 
  * (c) Copyright 2003-2007, Marvell International Ltd.
  * All Rights Reserved
  */

#include <windows.h>

#include "bt_def.h"
#include "bt_dev.h"
#include "bt_func.h"
#include "bt_debug.h"
#include "igxBug.h"


/********************************************************
        Local Variables
********************************************************/

/********************************************************
        Global Variables
********************************************************/

/********************************************************
        Local Functions
********************************************************/

/********************************************************
        Global Functions
********************************************************/
MRVL_STATUS AllocateRxQ(PMRVDRV_ADAPTER Adapter)
{
    BOOLEAN bSuccess = TRUE;
    PACKET_QUEUE_NODE *pTempNode;
    UCHAR *pTempPacketBuf = NULL;
    ULONG ulBufSize;
    int i;

    //
    // Initialize Recieve Queue
    //
    EnterCriticalSection(&Adapter->RxPacketFreeQLock);
    InitializeQKeeper(&Adapter->RxPacketFreeQueue);
    LeaveCriticalSection(&Adapter->RxPacketFreeQLock);

    EnterCriticalSection(&Adapter->RxPacketPendQLock);
    InitializeQKeeper(&Adapter->RxPacketPendQueue);
    LeaveCriticalSection(&Adapter->RxPacketPendQLock);

    // Allocate the packet queue node
    ulBufSize = sizeof(PACKET_QUEUE_NODE) * MRVDRV_RX_PKT_QUEUE_DEPTH;
    Adapter->RxPacketQueueNode = (PACKET_QUEUE_NODE *)MRVDRV_ALLOC_MEM(ulBufSize);
    if (Adapter->RxPacketQueueNode == NULL)
    {
        bSuccess = FALSE;
        goto exit;
    }
    MrvlZeroMemory(Adapter->RxPacketQueueNode, ulBufSize);

    // Allocate memory buffers 
    for (i = 0; i < MRVDRV_RX_PKT_QUEUE_DEPTH; i++)
    {
        pTempNode = ((PACKET_QUEUE_NODE *)(Adapter->RxPacketQueueNode) + i);
 
        // Allocate packet buffer
        pTempPacketBuf = (PUCHAR)MRVDRV_ALLOC_MEM(BT_UPLD_SIZE);
        if (!pTempPacketBuf)
        {
            bSuccess = FALSE;
            goto exit;
        }
        MrvlZeroMemory(pTempPacketBuf, BT_UPLD_SIZE);

        // Chain packet buffer to packet queue node
        pTempNode->pPacketBuf = pTempPacketBuf;

        // Append packet to Rx Packet Free Q
        EnterCriticalSection(&Adapter->RxPacketFreeQLock);
        InsertQNodeAtTail(&Adapter->RxPacketFreeQueue, (PACKET_QUEUE_NODE *)pTempNode);
        LeaveCriticalSection(&Adapter->RxPacketFreeQLock);
    }

    return MRVL_STATUS_SUCCESS;

exit:
    if (!bSuccess) {
        FreeRxQ(Adapter);
        return MRVL_STATUS_FAILURE;
    }

    return MRVL_STATUS_SUCCESS;
}

MRVL_STATUS ResetRxQ(PMRVDRV_ADAPTER Adapter)
{
    PACKET_QUEUE_NODE *pTempNode;
    int i;


    DBGPRINT(DBG_BUF, (L"Reset Rx Packet Queue\n\r"));

    // Reset Rx Packet Free Queue
    EnterCriticalSection(&Adapter->RxPacketFreeQLock);
    InitializeQKeeper(&Adapter->RxPacketFreeQueue);
    LeaveCriticalSection(&Adapter->RxPacketFreeQLock);

    EnterCriticalSection(&Adapter->RxPacketPendQLock);
    InitializeQKeeper(&Adapter->RxPacketPendQueue);
    LeaveCriticalSection(&Adapter->RxPacketPendQLock);

    for (i = 0; i < MRVDRV_RX_PKT_QUEUE_DEPTH; i++)
    {
        pTempNode = ((PACKET_QUEUE_NODE *)(Adapter->RxPacketQueueNode) + i);

        pTempNode->Next = NULL;

        if (pTempNode->pPacketBuf)
            MrvlZeroMemory(pTempNode->pPacketBuf, BT_UPLD_SIZE);

        // Append packet node to Rx Packet Free Q
        EnterCriticalSection(&Adapter->RxPacketFreeQLock);
        InsertQNodeAtTail(&Adapter->RxPacketFreeQueue, (PACKET_QUEUE_NODE *)pTempNode);
        LeaveCriticalSection(&Adapter->RxPacketFreeQLock);
    }

    return MRVL_STATUS_SUCCESS;
}

MRVL_STATUS FreeRxQ(PMRVDRV_ADAPTER Adapter)
{
    PACKET_QUEUE_NODE *pTempNode;
    int i;


    if (Adapter->RxPacketQueueNode == NULL)
        return MRVL_STATUS_SUCCESS;
                                     
    // Release memory buffers
    for (i = 0; i < MRVDRV_RX_PKT_QUEUE_DEPTH; i++)
    {
        pTempNode = ((PACKET_QUEUE_NODE *)(Adapter->RxPacketQueueNode) + i);

        if (pTempNode->pPacketBuf)
        {
            MRVDRV_FREE_MEM((PVOID)pTempNode->pPacketBuf);
        }
    }

    // Release Rx Packet Queue
    if (Adapter->RxPacketQueueNode)
    {
        MRVDRV_FREE_MEM((PVOID *)Adapter->RxPacketQueueNode);
    }

    return MRVL_STATUS_SUCCESS;
}

PACKET_QUEUE_NODE *GetRxPacketFromQ(PMRVDRV_ADAPTER Adapter)
{
    PACKET_QUEUE_NODE *pTempNode;

    if (!Adapter)
        return NULL;

    if (!IsQEmpty(&Adapter->RxPacketFreeQueue))
    { 
        EnterCriticalSection(&Adapter->RxPacketFreeQLock);
        pTempNode = (PACKET_QUEUE_NODE *)PopFirstQNode(&Adapter->RxPacketFreeQueue);    
        LeaveCriticalSection(&Adapter->RxPacketFreeQLock);

        if (pTempNode->pPacketBuf)
            MrvlZeroMemory(pTempNode->pPacketBuf, BT_UPLD_SIZE);

        return pTempNode;
    }
    else
    {
        /* There is NO free packet */
        return (PACKET_QUEUE_NODE *)NULL;
    }
}

VOID ReturnRxPacketToQ(PMRVDRV_ADAPTER Adapter,
                       PACKET_QUEUE_NODE *pPacketNode)
{
    if (!pPacketNode)
        return;
  
    EnterCriticalSection(&Adapter->RxPacketFreeQLock);
    InsertQNodeAtTail(&Adapter->RxPacketFreeQueue, (PACKET_QUEUE_NODE *)pPacketNode);
    LeaveCriticalSection(&Adapter->RxPacketFreeQLock);

    return;
}

VOID HandleRxReadyEvent(PMRVDRV_ADAPTER Adapter)
{
    USHORT buf_len = 0;
    ULONG type;
    PUCHAR pTempPacket = NULL;
    IF_API_STATUS ifStatus;
    PACKET_QUEUE_NODE *pPacketNode;

    DBGPRINT(DBG_RX, (L"++HandleRxReadyEvent++\n\r"));

    /* Read the length of data to be transferred */
    If_GetLengthOfDataBlock(Adapter, BT_FUNC, &buf_len);
    if (buf_len <= SDIO_HEADER_LEN || buf_len > ALLOC_BUF_SIZE)
    {
        DBGPRINT(DBG_RX, (L"Invalid packet length: %d\n\r", buf_len));
        return;
    }

    pPacketNode = (PACKET_QUEUE_NODE *)GetRxPacketFromQ(Adapter);
    if (pPacketNode == NULL)
    {
        /* If there is no free packet, read the packet then drop it */
        DBGPRINT(DBG_RX|DBG_WARNING, (L"NO Free Packet, will drop this packet !\n\r"));
        pTempPacket = (PUCHAR)MRVDRV_ALLOC_MEM(buf_len);
        if (NULL == pTempPacket)
        {
            DBGPRINT(DBG_RX|DBG_ERROR, (L"Memory Allocation Fail !\n\r"));
            return;
        }

        ifStatus = If_GetDataBlock(Adapter, buf_len, (UCHAR *)pTempPacket);
        if (!IF_IS_SUCCESS(ifStatus))
        {
            DBGPRINT(DBG_RX, (L"Get Data Block Fail !\n\r"));
        }

        HexDump(DBG_RX_DUMP, "Rx BUFFER : ", (PUCHAR)pTempPacket, buf_len);

        Adapter->RxPacketDropCnt++;
        DBGPRINT(DBG_RX|DBG_WARNING, (L"Packet dropped  %d !\n\r", Adapter->RxPacketDropCnt));
        MRVDRV_FREE_MEM((PVOID)pTempPacket);
        return;
    }

    ifStatus = If_GetDataBlock(Adapter, buf_len, (PUCHAR)pPacketNode->pPacketBuf);
    if (!IF_IS_SUCCESS(ifStatus))
    {
        DBGPRINT(DBG_RX|DBG_ERROR, (L"Get Data Block Fail !\n\r"));
        ReturnRxPacketToQ(Adapter, (PACKET_QUEUE_NODE *)pPacketNode);
        goto exit;
    }

    HexDump(DBG_RX_DUMP, "Rx BUFFER : ", (PUCHAR)pPacketNode->pPacketBuf, buf_len);

    /*  This is SDIO specific header
     *  length: byte[2][1][0], 
     *  type: byte[3] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) 
     */
    type = pPacketNode->pPacketBuf[3];
    switch (type)
    {
        case HCI_ACLDATA_PKT:
            Adapter->RxHCIAclDataCnt++;
            DBGPRINT(DBG_RX, (L"HCI_ACLDATA_PKT %d\n\r", Adapter->RxHCIAclDataCnt));

            // Append packet to Rx Packet Pending Q
            EnterCriticalSection(&Adapter->RxPacketPendQLock);
            InsertQNodeAtTail(&Adapter->RxPacketPendQueue, (PACKET_QUEUE_NODE *)pPacketNode);
            LeaveCriticalSection(&Adapter->RxPacketPendQLock);

            SetEvent(Adapter->hReadPacketEvent);
            break;

        case HCI_SCODATA_PKT:
            Adapter->RxHCIScoDataCnt++;
            DBGPRINT(DBG_RX, (L"HCI_SCODATA_PKT, cnt: %d\n\r", Adapter->RxHCIScoDataCnt));

            // Append packet to Rx Packet Pending Q
            EnterCriticalSection(&Adapter->RxPacketPendQLock);
            InsertQNodeAtTail(&Adapter->RxPacketPendQueue, (PACKET_QUEUE_NODE *)pPacketNode);
            LeaveCriticalSection(&Adapter->RxPacketPendQLock);

            SetEvent(Adapter->hReadPacketEvent);
            break;

        case HCI_EVENT_PKT:
            Adapter->RxHCIEventPktCnt++;
            DBGPRINT(DBG_RX, (L"HCI_EVENT_PKT, cnt: %d\n\r", Adapter->RxHCIEventPktCnt));

            // Append packet to Rx Packet Pending Q
            EnterCriticalSection(&Adapter->RxPacketPendQLock);
            InsertQNodeAtTail(&Adapter->RxPacketPendQueue, (PACKET_QUEUE_NODE *)pPacketNode);
            LeaveCriticalSection(&Adapter->RxPacketPendQLock);

            SetEvent(Adapter->hReadPacketEvent);
            break;

        case HCI_VENDOR_PKT:
            Adapter->RxVendorPktCnt++;
            DBGPRINT(DBG_RX, (L"HCI_VENDOR_PKT, cnt: %d\n\r", Adapter->RxVendorPktCnt));

            /* Process Marvell BT Command Response and Event */
            /* Skip SDIO header and process BT Event only    */
            HandleBTVendorPacket(Adapter,
                                (PUCHAR)&pPacketNode->pPacketBuf[SDIO_HEADER_LEN]);

            /* Return this packet node to Free Q */
            ReturnRxPacketToQ(Adapter, (PACKET_QUEUE_NODE *)pPacketNode);
            break;

        default: 
            Adapter->RxUnknownPktCnt++;
            DBGPRINT(DBG_RX, (L"Unknown PKT, type: 0x%x, cnt: %d\n\r", type, Adapter->RxUnknownPktCnt));
            /* Return this packet node to Free Q */
            ReturnRxPacketToQ(Adapter, (PACKET_QUEUE_NODE *)pPacketNode);
            break;
    }

exit:

    return;
}


BOOL HandleHCIReadPacket(PMRVDRV_ADAPTER Adapter,
                         PUCHAR pBuffer,
                         PUINT pPacketSize,
                         PUCHAR pType)
{
    BOOL fRetVal = TRUE;
    SD_TRANSPORT_HEADER *pHeader;
    PACKET_QUEUE_NODE *pPacketNode = NULL;

    DBGPRINT(DBG_RX, (L"++ HandleHCIReadPacket ++\n\r"));


    DBGPRINT(DBG_RX, (L"Wait for Read Packet Event ..... \n\r"));
    WaitForSingleObject(Adapter->hReadPacketEvent, INFINITE);

    if (Adapter->bIsOpenConnection == FALSE)
    {
        DBGPRINT(DBG_RX, (L"Unblock rx when HCI_Disconnection() be called !\n\r"));
        return FALSE;
    }

    /* For LTK 3070 Bluetooth Test */
    if (Adapter->CardRemoved == TRUE)
    {
        DBGPRINT(DBG_RX|DBG_ERROR, (L"SDIO Card Removed !\n\r"));
        return FALSE;
    }

check_Q:
    if (!IsQEmpty(&Adapter->RxPacketPendQueue))
    {
        //DBGPRINT(DBG_RX, (L"Packet exist in Packet Pending Queue\n\r"));
        EnterCriticalSection(&Adapter->RxPacketPendQLock);
        pPacketNode = (PACKET_QUEUE_NODE *)PopFirstQNode(&Adapter->RxPacketPendQueue);    
        LeaveCriticalSection(&Adapter->RxPacketPendQLock);
    }
    else
    {
        DBGPRINT(DBG_RX|DBG_WARNING, (L"There should be at least one packet in Queue but Queue is empty now !\n\r"));
        DBGPRINT(DBG_RX, (L"Wait for Read Packet Event ..... \n\r"));
        WaitForSingleObject(Adapter->hReadPacketEvent, INFINITE);
        goto check_Q;
    }

    pHeader = (SD_TRANSPORT_HEADER *)pPacketNode->pPacketBuf;

    if ((pHeader->u.AsULONG & 0xFFFFFF) <= 4)
    {
        DBGPRINT(DBG_RX, (L"Received a zero-length packet.\n\r"));
        fRetVal = FALSE;
        goto exit;
    }

    // SDIO service ID maps directly to HCI_TYPE
    *pPacketSize = (pHeader->u.AsULONG & 0xFFFFFF);
    *pType = (BYTE)pHeader->u.AsUCHAR.ServiceID;
    
    DBGPRINT(DBG_RX, (L"Received packet - Size:%d, Type:%d\n\r", *pPacketSize, *pType));

    if (*pPacketSize > MRVL_BT_PACKET_SIZE_R)
    {
        DBGPRINT(DBG_RX, (L"Header is specifying invalid size of data.\n\r"));
        fRetVal = FALSE;
        goto exit;
    }

    memmove(pBuffer, &pPacketNode->pPacketBuf[0], *pPacketSize);
    
exit:
    ReturnRxPacketToQ(Adapter, pPacketNode);
    if (!IsQEmpty(&Adapter->RxPacketPendQueue))
        SetEvent(Adapter->hReadPacketEvent);
    return fRetVal;
}
